篇首语:本文由编程笔记#小编为大家整理,主要介绍了Redis 进阶 -- 持久化(RDB持久化AOF持久化RDB-AOF混合持久化无持久化)相关的知识,希望对你有一定的参考价值。
Redis与传统数据库的一个主要区别在于,Redis把所有数据都存储在内存中,而传统数据库通常只会把数据的索引存储在内存中,并将实际的数据存储在硬盘中。
虽然Redis的数据存储方式使得用户可以以极快的速度读写服务器中的数据,但由于内存属于易失存储器(volatile storage),它记录的所有数据在系统断电之后就会丢失,这对于想把Redis用作数据库而不仅仅是缓存的用户来说是不愿意看到的。
为了满足不同的持久化需求,Redis提供了RDB持久化、AOF持久化和RDB-AOF混合持久化等多种持久化方式以供用户选择。如果用户有需要,也可以完全关闭持久化功能,让服务器处于无持久化状态。
RDB持久化是Redis默认使用的持久化功能,该功能可以创建出一个经过压缩的二进制文件,其中包含了服务器在各个数据库中存储的键值对数据等信息。RDB持久化产生的文件都以.rdb后缀结尾,其中rdb代表Redis DataBase(Redis数据库)。
Redis提供了多种创建RDB文件的方法,用户既可以使用SAVE命令或者BGSAVE命令手动创建RDB文件,也可以通过设置save配置选项让服务器在满足指定条件时自动执行BGSAVE命令。
用户可以通过执行SAVE命令,要求Redis服务器以同步方式创建出一个记录了服务器当前所有数据库数据的RDB文件。SAVE命令是一个无参数命令,它在创建RDB文件成功时将返回OK作为结果。
接收到SAVE命令的Redis服务器将遍历数据库包含的所有数据库,并将各个数据库包含的键值对全部记录到RDB文件中。在SAVE命令执行期间,Redis服务器将阻塞,直到RDB文件创建完毕为止。如果Redis服务器在执行SAVE命令时已经拥有了相应的RDB文件,那么服务器将使用新创建的RDB文件代替已有的RDB文件,这个过程如下图所示。
语法:
示例:
说明:
复杂度:O(N),其中N为Redis服务器所有数据库包含的键值对总数量。
因为SAVE命令在执行时会阻塞整个服务器,所以用户在使用该命令创建RDB文件期间将无法为其他客户端提供服务。为了解决这个问题,Redis提供了SAVE命令的异步版本BGSAVE命令:这个命令与SAVE命令一样都是无参数命令,它与SAVE命令的不同之处在于,BGSAVE不会直接使用Redis服务器进程创建RDB文件,而是使用子进程创建RDB文件。
当Redis服务器接收到用户发送的BGSAVE命令时,将执行以下操作:
下图展示了这一执行过程:
因为BGSAVE命令创建RDB文件的操作是由子进程以异步方式执行的,所以当用户在客户端执行这个命令时,服务器将立即向客户端返回OK,然后才会在后台开始具体的RDB文件创建操作:
因为BGSAVE命令是以异步方式执行的,所以Redis服务器在BGSAVE命令执行期间仍然可以继续处理其他客户端发送的命令请求。不过需要注意的是,虽然BGSAVE命令不会像SAVE命令那样一直阻塞Redis服务器,但由于执行BGSAVE命令需要创建子进程,所以父进程占用的内存数量越大,创建子进程这一操作耗费的时间也会越长,因此Redis服务器在执行BGSAVE命令时,仍然可能会由于创建子进程而被短暂地阻塞。
说明:
复杂度:O(N),其中N为Redis服务器所有数据库包含的键值对总数量。
用户除了可以使用SAVE命令和BGSAVE命令手动创建RDB文件之外,还可以通过设置save选项,让Redis服务器在满足指定条件时自动执行BGSAVE命令。
配置文件位置:redis安装目录/redis.conf
save选项接受seconds和changes两个参数,前者用于指定触发持久化操作所需的时长,而后者则用于指定触发持久化操作所需的修改次数。简单来说,如果服务器在seconds秒之内,对其包含的各个数据库总共执行了至少changes次修改,那么服务器将自动执行一次BGSAVE命令。
示例:
比如,如果我们向服务器提供以下选项:
save 60 10000
那么当“服务器在60s秒之内至少执行了10000次修改”这一条件被满足时,服务器就会自动执行一次BGSAVE命令。
同时使用多个save选项:
Redis允许用户同时向服务器提供多个save选项,当给定选项中的任意一个条件被满足时,服务器就会执行一次BGSAVE。
比如,如果我们向服务器提供以下选项:
save 6000 1
save 600 100
save 60 10000
那么当以下任意一个条件被满足时,服务器就会执行一次BGSAVE命令:
注意,为了避免由于同时使用多个触发条件而导致服务器过于频繁地执行BGSAVE命令,Redis服务器在每次成功创建RDB文件之后,负责自动触发BGSAVE命令的时间计数器以及修改次数计数器都会被清零并重新开始计数:无论这个RDB文件是由自动触发的BGSAVE命令创建的,还是由用户执行的SAVE命令或BGSAVE命令创建的,都是如此。
因为SAVE命令在创建RDB文件期间会阻塞Redis服务器,所以如果我们需要在创建RDB文件的同时让Redis服务器继续为其他客户端服务,那么就只能使用BGSAVE命令来创建RDB文件。
因为SAVE命令无须创建子进程,它不会因为创建子进程而消耗额外的内存,所以在维护离线的Redis服务器时,使用SAVE命令能够比使用BGSAVE命令更快地完成创建RDB文件的工作。
RDB文件的总体结构,整个文件共分为7个部分:
RDB文件标识符:文件最开头的部分为RDB文件标识符,这个标识符的内容为"REDIS"这5个字符。Redis服务器在尝试载入RDB文件的时候,可以通过这个标识符快速地判断该文件是否为真正的RDB文件。
版本号:跟在RDB文件标识符之后的是RDB文件的版本号,这个版本号是一个字符串格式的数字,长度为4个字符。目前最新的RDB文件版本为第9版,因此RDB文件的版本号将为字符串"0009"。不同版本的RDB文件在结构上都会有一些不同,总的来说,新版RDB文件都会在旧版RDB文件的基础上添加更多信息,因此RDB文件的版本越新,RDB文件的结构就越复杂。关于RDB文件,需要说明的另外一点是新版Redis服务器总是能够向下兼容旧版Redis服务器生成的RDB文件。
设备附加信息:RDB文件的设备附加信息部分记录了生成RDB文件的Redis服务器及其所在平台的信息,比如服务器的版本号、宿主机器的架构、创建RDB文件时的时间戳、服务器占用的内存数量等。
数据库数据:RDB文件的数据库数据部分记录了Redis服务器存储的0个或任意多个数据库的数据,当这个部分包含多数个数据库的数据时,各个数据库的数据将按照数据库号码从小到大进行排列,比如,0号数据库的数据将排在最前面,紧接着是1号数据库的数据,然后是2号数据库的数据,以此类推,下图展示了这一排列顺序。
Lua脚本缓存:如果Redis服务器启用了复制功能,那么服务器将在RDB文件的Lua脚本缓存部分保存所有已被缓存的Lua脚本。这样一来,从服务器在载入RDB文件完成数据同步之后,就可以继续执行主服务器发来的EVALSHA命令了。
EOF:RDB文件的EOF部分用于标识RDB正文内容的末尾,它的实际值为二进制值0xFF。当Redis服务器读取到EOF的时候,它知道RDB文件的正文部分已经全部读取完毕了。
CRC64校验和:RDB文件的末尾是一个以无符号64位整数表示的CRC64校验和,比如5097628732947693614。Redis服务器在读入RDB文件时会通过这个校验和来快速地检查RDB文件是否有出错或者损坏的情况出现。
数据库信息结构:
前面提到过,RDB文件的数据库数据部分包含了任意多个数据库的数据,其中每个数据库都由下图所示的4个部分组成。
首先,第一部分以数字形式记录了数据库的号码,比如0。Redis服务器在读入RDB文件数据时,会根据这个号码切换至相应的数据库,从而确保键值对会被载入正确的数据库中。
在之后的两个部分,RDB文件会使用两个数字,分别记录数据库包含的键值对总数量以及数据库中带有过期时间的键值对数量。Redis服务器将根据这两个数字,以尽可能优化的方式创建数据库的内部数据结构。
在最后一个部分,RDB文件将以无序方式记录数据库包含的所有键值对。具体来说,数据库中的每个键值对都会被划分为最多5个部分,如图所示。
每个键值对开头的第一部分记录的是可能存在的过期时间,这是一个毫秒级精度的UNIX时间戳。
之后的LRU信息或者LFU信息分别用于实现可选的LRU算法或者LFU算法,并且因为Redis只能选择一种键淘汰算法,所以这两项信息将不会同时出现,最多只会出现其中一种。
至于最后三个部分则分别记录了键值对的类型(比如字符串、列表、散列等)以及键和值。
首先,当Redis服务器启动时,它会在工作目录中查找是否有RDB文件出现,如果有就打开它,然后读取文件的内容并执行以下载入操作:
下图展示了这一数据载入流程。
RDB文件记录的是服务器在开始创建文件的那一刻,服务器中包含的所有键值对数据,这种数据持久化方式通常被称为时间点快照(point-in-time snapshot)。时间点快照持久化的一个特点是,系统在停机时将丢失最后一次成功实施持久化之后的所有数据。对于一个只使用RDB持久化的Redis服务器来说,服务器停机时丢失的数据量将取决于最后一次成功执行的RDB持久化操作,以及该操作开始执行的时间。
总的来说,无论用户使用的是SAVE命令还是BGSAVE命令,停机时服务器丢失的数据量将取决于创建RDB文件的时间间隔:间隔越长,停机时丢失的数据也就越多。
然而矛盾之处在于,RDB持久化是一种全量持久化操作,它在创建RDB文件时需要存储整个服务器包含的所有数据,并因此消耗大量计算资源和内存资源,所以用户是不太可能通过增大RDB文件的生成频率来保证数据安全的。
举个例子,虽然从技术上来说,用户可以在每次执行写命令之后都执行一次SAVE命令,以此来保证数据处于绝对安全的状态,但这样一来Redis服务器的性能将下降至无法正常使用的水平。相反,用户如果想要保证服务器的性能处于合理水平,就不能过于频繁地创建RDB文件,这样一来,也就不可避免地会出现因为停机而丢失大量数据的情况。
从RDB持久化的特征来看,它更像是一种数据备份手段而非一种普通的数据持久化手段。为了解决RDB持久化在停机时可能会丢失大量数据这一问题,并提供一种真正符合用户预期的持久化功能,Redis推出了AOF持久化模式。
与全量式的RDB持久化功能不同,AOF提供的是增量式的持久化功能,这种持久化的核心原理在于:服务器每次执行完写命令之后,都会以协议文本的方式将被执行的命令追加到AOF文件的末尾。这样一来,服务器在停机之后,只要重新执行AOF文件中保存的Redis命令,就可以将数据库恢复至停机之前的状态。
用户可以通过服务器的appendonly选项来决定是否打开AOF持久化功能:
如果用户想要开启AOF持久化功能,那么只需要将这个值设置为yes即可:
反之,如果用户想要关闭AOF持久化功能,那么只需要将这个值设置为no即可。
配置文件位置:redis安装目录/redis.conf
当AOF持久化功能处于打开状态时,Redis服务器在默认情况下将创建一个名为appendonly. aof的文件作为AOF文件。
为了提高程序的写入性能,现代化的操作系统通常会把针对硬盘的多次写操作优化为一次写操作。具体的做法是,当程序调用write系统调用对文件进行写入时,系统并不会直接把数据写入硬盘,而是会先将数据写入位于内存的缓冲区中,等到指定的时限到达或者满足某些写入条件时,系统才会执行flush系统调用,将缓冲区中的数据冲洗至硬盘。
这种优化机制虽然提高了程序的性能,但是也给程序的写入操作带来了不确定性,特别是对于AOF这样的持久化功能来说,AOF文件的冲洗机制将直接影响AOF持久化的安全性。为了消除上述机制带来的不确定性,Redis向用户提供了appendfsync选项,以此来控制系统冲洗AOF文件的频率:
appendfsync选项拥有always、everysec和no 3个值可选,它们代表的意义分别为:
这3种不同的冲洗策略不仅会直接影响服务器在停机时丢失的数据量,还会影响服务器在运行时的性能:
因为no策略给可能丢失的数据量带来了不确定性,而always策略对于安全性的追求又牺牲了服务器的性能,所以Redis使用everysec作为appendfsync选项的默认值。除非有明确的需求,否则用户不应该随意修改appendfsync选项的值。
配置文件位置:redis安装目录/redis.conf
随着服务器不断运行,被执行的命令将变得越来越多,而负责记录这些命令的AOF文件也会变得越来越大。与此同时,如果服务器曾经对相同的键执行过多次修改操作,那么AOF文件中还会出现多个冗余命令。
冗余命令的存在不仅增加了AOF文件的体积,并且因为Redis服务器在停机之后需要通过重新执行AOF文件中保存的命令来恢复数据,所以AOF文件中的冗余命令越多,恢复数据时耗费的时间也会越多。为了减少冗余命令,并提供数据恢复操作的执行速度,Redis提供了AOF重写功能,该功能能够生成一个全新的AOF文件,并且文件中只包含恢复当前数据库所需的尽可能少的命令。
只包含对数据库数据起决定性作业的命令,不去管过程。比如对于一下命令:
set num 1
set num 2
set num 3
...
set num 99999
因为每次set命令都会覆盖之前的值,所以AOF重写功能生成的AOF文件只会包含set num 99999
命令。
用户可以通过执行BGREWRITEAOF命令或者设置相关的配置选项来触发AOF重写操作。
用户可以通过执行BGREWRITEAOF命令显式地触发AOF重写操作,该命令是一个无参数命令:
BGREWRITEAOF命令是一个异步命令,Redis服务器在接收到该命令之后会创建出一个子进程,由它扫描整个数据库并生成新的AOF文件。当新的AOF文件生成完毕,子进程就会退出并通知Redis服务器(父进程),然后Redis服务器就会使用新的AOF文件代替已有的AOF文件,借此完成整个重写操作。
关于BGREWRITEAOF还有两点需要注意:
说明:
复杂度:O(N),其中N为Redis服务器所有数据库包含的键值对总数量。
用户除了可以手动执行BGREWRITEAOF命令创建新的AOF文件之外,还可以通过设置以下两个配置选项让Redis自动触发BGREWRITEAOF命令:
其中auto-aof-rewrite-min-size选项用于设置触发自动AOF文件重写所需的最小AOF文件体积,当AOF文件的体积小于给定值时,服务器将不会自动执行BGREWRITEAOF命令。在默认情况下,该选项的值为:
也就是说,如果AOF文件的体积小于64MB,那么Redis将不会自动执行BGREWRI-TEAOF命令。
至于另一个选项,它控制的是触发自动AOF文件重写所需的文件体积增大比例。举个例子,对于该选项的默认值:
表示如果当前AOF文件的体积比最后一次AOF文件重写之后的体积增大了一倍(100%),那么将自动执行一次BGREWRITEAOF命令。如果Redis服务器刚刚启动,还没有执行过AOF文件重写操作,那么启动服务器时使用的AOF文件的体积将被用作最后一次AOF文件重写的体积。
举个例子,如果服务器启动时AOF文件的体积为200MB,而auto-aof-rewrite-percentage选项的值为100,那么当AOF文件的体积增大至超过400MB时,服务器就会自动进行一次AOF重写。与此类似,在同样设置下,如果AOF文件的体积从最后一次重写之后的300MB增大至超过600MB,那么服务器将再次执行AOF重写操作。
说明:
配置文件位置:redis安装目录/redis.conf
与RDB持久化可能会丢失大量数据相比,AOF持久化的安全性要高得多:通过使用everysec选项,用户可以将数据丢失的时间窗口限制在1s之内。
但是与RDB持久化相比,AOF持久化也有相应的缺点:
由于RDB持久化和AOF持久化都有各自的优缺点,因此在很长一段时间里,如何选择合适的持久化方式成了很多Redis用户面临的一个难题。
为了解决这个问题,Redis从4.0版本开始引入RDB-AOF混合持久化模式,这种模式是基于AOF持久化模式构建而来的——如果用户打开了服务器的AOF持久化功能,并且将
选项的值设置成了yes,那么Redis服务器在执行AOF重写操作时,就会像执行BGSAVE命令那样,根据数据库当前的状态生成出相应的RDB数据,并将这些数据写入新建的AOF文件中,至于那些在AOF重写开始之后执行的Redis命令,则会继续以协议文本的方式追加到新AOF文件的末尾,即已有的RDB数据的后面。
换句话说,在开启了RDB-AOF混合持久化功能之后,服务器生成的AOF文件将由两个部分组成,其中位于AOF文件开头的是RDB格式的数据,而跟在RDB数据后面的则是AOF格式的数据,如图所示:
当一个支持RDB-AOF混合持久化模式的Redis服务器启动并载入AOF文件时,它会检查AOF文件的开头是否包含了RDB格式的内容:
下图展示了这一判断过程:
通过使用RDB-AOF混合持久化功能,用户可以同时获得RDB持久化和AOF持久化的优点:服务器既可以通过AOF文件包含的RDB数据来实现快速的数据恢复操作,又可以通过AOF文件包含的AOF数据来将丢失数据的时间窗口限制在1s之内。
配置文件位置:redis安装目录/redis.conf
在Redis 4.0的RDB-AOF混合持久化功能出现之前,不少追求安全性的Redis使用者都会同时使用RDB持久化和AOF持久化,但随着RDB-AOF混合持久化功能的推出,同时使用两种持久化功能已经不再必要。
如果用户使用的是Redis 4.0之前的版本,那么同时使用RDB持久化和AOF持久化仍然是可行的,只要注意以下问题即可:
总的来说,在数据持久化这个问题上,Redis 4.0及之后版本的使用者都应该优先使用RDB-AOF混合持久化;对于Redis 4.0之前版本的使用者,因为RDB持久化更接近传统意义上的数据备份功能,而AOF持久化则更接近于传统意义上的数据持久化功能,所以如果用户不知道自己具体应该使用哪种持久化功能,那么可以优先选用AOF持久化作为数据持久化手段,并将RDB持久化用作辅助的数据备份手段。
如果用户想要彻底关闭这一默认的RDB持久化行为,让Redis服务器处于完全的无持久化状态,那么可以在服务器启动时向它提供以下配置选项:
配置文件位置:redis安装目录/redis.conf
这样一来,服务器将不会再进行默认的RDB持久化,从而使得服务器处于完全的无持久化状态中。处于这一状态的服务器在关机之后将丢失关机之前存储的所有数据,这种服务器可以用作单纯的内存缓存服务器。
用户可以通过执行SHUTDOWN命令来关闭Redis服务器。
在默认情况下,当Redis服务器接收到SHUTDOWN命令时,它将执行以下动作:
因为Redis服务器在接收到SHUTDOWN命令并关闭自身的过程中,会根据配置选项来决定是否执行数据保存操作,所以只要服务器启用了持久化功能,那么使用SHUTDOWN命令来关闭服务器就不会造成任何数据丢失。
在默认情况下,服务器在执行SHUTDOWN命令时,是否执行持久化操作是由服务器的配置选项决定的。
但是在有需要时,用户也可以使用SHUTDOWN命令提供的save选项或者nosave选项,显式地指示服务器在关闭之前是否需要执行持久化操作:
如果用户给定的是save选项,那么无论服务器是否启用了持久化功能,服务器都会在关闭之前执行一次持久化操作。下表展示了服务器在不同持久化配置下,执行SHUTDOWN save命令时的行为。
如果用户给定的是nosave选项,那么服务器将不执行持久化操作,直接关闭服务器。在这种情况下,如果服务器在关闭之前曾经修改过数据库,那么它将丢失那些尚未保存的数据。
复杂度:O(N),其中N为关闭服务器时需要持久化的键值对数量。